/* 
 * Copyright 2019 Matt Jamesson <scifidryer@gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package modbusmechanic;

import modbusmechanic.gateway.GatewayFrame;
import com.intelligt.modbus.jlibmodbus.exception.*;
import com.intelligt.modbus.jlibmodbus.utils.DataUtils;
import com.intelligt.modbus.jlibmodbus.msg.base.*;
import com.intelligt.modbus.jlibmodbus.msg.response.*;
import javax.swing.*;
import com.intelligt.modbus.jlibmodbus.serial.*;
import java.io.*;
import java.util.*;
/**
 *
 * @author Matt Jamesson
 */
public class PacketFrame extends javax.swing.JFrame {

    /**
     * Creates new form PacketFrame
     */
    public ModbusResponse lastResponse = null;
    public int lastResponseType = 0;
    public int lastFunctionCode = 0;
    public int mediumType = 0;
    public ArrayList<String[]> bookmarkList = new ArrayList<String[]>();
    BitsFrame bitsFrame = null;
    boolean hasTtyPorts = false;
    public PacketFrame()
    {
        try
        {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
            {
                if ("Windows".equals(info.getName()))
                {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }
        catch (ClassNotFoundException ex)
        {
            java.util.logging.Logger.getLogger(PacketFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (InstantiationException ex)
        {
            java.util.logging.Logger.getLogger(PacketFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (IllegalAccessException ex)
        {
            java.util.logging.Logger.getLogger(PacketFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (javax.swing.UnsupportedLookAndFeelException ex)
        {
            java.util.logging.Logger.getLogger(PacketFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        
        
        initComponents();
        
        if (!ModbusMechanic.isSerialAvailable())
        {
            rtuMsgButton.setEnabled(false);
            comPortSelector.setEnabled(false);
            baudRateSelector.setEnabled(false);
            dataBitsField.setEnabled(false);
            stopBitsField.setEnabled(false);
            paritySelector.setEnabled(false);
            rtuSerialMonitorItem.setEnabled(false);
        }
        functionSelector.setVisible(true);
        jPanel5.setVisible(false);
        
        
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        buttonGroup2 = new javax.swing.ButtonGroup();
        buttonGroup3 = new javax.swing.ButtonGroup();
        typePanel = new javax.swing.JPanel();
        jLabel11 = new javax.swing.JLabel();
        tcpMsgButton = new javax.swing.JRadioButton();
        rtuMsgButton = new javax.swing.JRadioButton();
        displayTtySerial = new javax.swing.JCheckBox();
        jSeparator1 = new javax.swing.JSeparator();
        serialPanel = new javax.swing.JPanel();
        jLabel14 = new javax.swing.JLabel();
        jLabel12 = new javax.swing.JLabel();
        comPortSelector = new javax.swing.JComboBox<>();
        jLabel13 = new javax.swing.JLabel();
        baudRateSelector = new javax.swing.JComboBox<>();
        jLabel15 = new javax.swing.JLabel();
        dataBitsField = new javax.swing.JTextField();
        jLabel16 = new javax.swing.JLabel();
        stopBitsField = new javax.swing.JTextField();
        jLabel17 = new javax.swing.JLabel();
        paritySelector = new javax.swing.JComboBox<>();
        jSeparator2 = new javax.swing.JSeparator();
        modbusPanel = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        destHostField = new javax.swing.JTextField();
        jLabel18 = new javax.swing.JLabel();
        portField = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        slaveNodeField = new javax.swing.JTextField();
        jLabel5 = new javax.swing.JLabel();
        jLayeredPane1 = new javax.swing.JLayeredPane();
        jPanel5 = new javax.swing.JPanel();
        functionCodeField = new javax.swing.JTextField();
        functionSelector = new javax.swing.JComboBox<>();
        modbusPanel2 = new javax.swing.JPanel();
        jLabel6 = new javax.swing.JLabel();
        registerField = new javax.swing.JTextField();
        jLabel3 = new javax.swing.JLabel();
        transactionField = new javax.swing.JTextField();
        jLabel4 = new javax.swing.JLabel();
        protoIdField = new javax.swing.JTextField();
        jLabel7 = new javax.swing.JLabel();
        quantityField = new javax.swing.JTextField();
        jSeparator3 = new javax.swing.JSeparator();
        messagePanel = new javax.swing.JPanel();
        jLabel8 = new javax.swing.JLabel();
        customMessageButton = new javax.swing.JRadioButton();
        readFloatButton = new javax.swing.JRadioButton();
        asciiReadButton = new javax.swing.JRadioButton();
        u16ReadButton = new javax.swing.JRadioButton();
        u32ReadButton = new javax.swing.JRadioButton();
        messagePanel2 = new javax.swing.JPanel();
        transmitPacketButton = new javax.swing.JButton();
        jSeparator4 = new javax.swing.JSeparator();
        interpPanel = new javax.swing.JPanel();
        jLabel9 = new javax.swing.JLabel();
        byteSwapCheckbox = new javax.swing.JCheckBox();
        wordSwapCheckbox = new javax.swing.JCheckBox();
        jSeparator5 = new javax.swing.JSeparator();
        responsePanel = new javax.swing.JPanel();
        valueLabel = new javax.swing.JLabel();
        writeParentPane = new javax.swing.JPanel();
        writeValuePane = new javax.swing.JPanel();
        valueField = new javax.swing.JTextField();
        boolPane = new javax.swing.JPanel();
        offButton = new javax.swing.JRadioButton();
        onButton = new javax.swing.JRadioButton();
        blankPane = new javax.swing.JPanel();
        bitsButton = new javax.swing.JButton();
        jSeparator6 = new javax.swing.JSeparator();
        packetPanel = new javax.swing.JPanel();
        jLabel10 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        rawTextBox = new javax.swing.JTextArea();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu2 = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();
        rtuSerialMonitorItem = new javax.swing.JMenuItem();
        jMenuItem3 = new javax.swing.JMenuItem();
        jMenuItem2 = new javax.swing.JMenuItem();
        startModbusBridge = new javax.swing.JMenuItem();
        jMenu1 = new javax.swing.JMenu();
        addBookmarkItem = new javax.swing.JMenuItem();
        updateBookmarkItem = new javax.swing.JMenuItem();
        deleteBookmarkItem = new javax.swing.JMenuItem();
        bookmarksMenu = new javax.swing.JMenu();
        jMenu3 = new javax.swing.JMenu();
        jMenuItem4 = new javax.swing.JMenuItem();
        jMenuItem5 = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("modbusmechanic/Bundle"); // NOI18N
        setTitle(bundle.getString("PacketFrame.title")); // NOI18N
        setSize(new java.awt.Dimension(751, 700));
        getContentPane().setLayout(new javax.swing.BoxLayout(getContentPane(), javax.swing.BoxLayout.Y_AXIS));

        typePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        jLabel11.setText(bundle.getString("PacketFrame.jLabel11.text")); // NOI18N
        typePanel.add(jLabel11);

        buttonGroup2.add(tcpMsgButton);
        tcpMsgButton.setSelected(true);
        tcpMsgButton.setText(bundle.getString("PacketFrame.tcpMsgButton.text")); // NOI18N
        tcpMsgButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                tcpMsgButtonActionPerformed(evt);
            }
        });
        typePanel.add(tcpMsgButton);

        buttonGroup2.add(rtuMsgButton);
        rtuMsgButton.setText(bundle.getString("PacketFrame.rtuMsgButton.text")); // NOI18N
        rtuMsgButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                rtuMsgButtonActionPerformed(evt);
            }
        });
        typePanel.add(rtuMsgButton);

        displayTtySerial.setText(bundle.getString("PacketFrame.displayTtySerial.text")); // NOI18N
        displayTtySerial.setEnabled(false);
        displayTtySerial.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                displayTtySerialActionPerformed(evt);
            }
        });
        typePanel.add(displayTtySerial);

        getContentPane().add(typePanel);
        getContentPane().add(jSeparator1);

        jLabel14.setText(bundle.getString("PacketFrame.jLabel14.text")); // NOI18N
        serialPanel.add(jLabel14);

        jLabel12.setText(bundle.getString("PacketFrame.jLabel12.text")); // NOI18N
        serialPanel.add(jLabel12);

        comPortSelector.setModel(getPortNames());
        comPortSelector.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                comPortSelectorActionPerformed(evt);
            }
        });
        serialPanel.add(comPortSelector);

        jLabel13.setText(bundle.getString("PacketFrame.jLabel13.text")); // NOI18N
        serialPanel.add(jLabel13);

        baudRateSelector.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "4800", "9600", "14400", "19200", "38400", "57600" }));
        serialPanel.add(baudRateSelector);

        jLabel15.setText(bundle.getString("PacketFrame.jLabel15.text")); // NOI18N
        serialPanel.add(jLabel15);

        dataBitsField.setColumns(2);
        dataBitsField.setText(bundle.getString("PacketFrame.dataBitsField.text")); // NOI18N
        serialPanel.add(dataBitsField);

        jLabel16.setText(bundle.getString("PacketFrame.jLabel16.text")); // NOI18N
        serialPanel.add(jLabel16);

        stopBitsField.setColumns(2);
        stopBitsField.setText(bundle.getString("PacketFrame.stopBitsField.text")); // NOI18N
        serialPanel.add(stopBitsField);

        jLabel17.setText(bundle.getString("PacketFrame.jLabel17.text")); // NOI18N
        serialPanel.add(jLabel17);

        paritySelector.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "None", "Odd", "Even", "Mark", "Space" }));
        paritySelector.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                paritySelectorActionPerformed(evt);
            }
        });
        serialPanel.add(paritySelector);

        getContentPane().add(serialPanel);
        getContentPane().add(jSeparator2);

        modbusPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        jLabel1.setText(bundle.getString("PacketFrame.jLabel1.text")); // NOI18N
        modbusPanel.add(jLabel1);

        destHostField.setColumns(12);
        destHostField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                destHostFieldActionPerformed(evt);
            }
        });
        modbusPanel.add(destHostField);

        jLabel18.setText(bundle.getString("PacketFrame.jLabel18.text")); // NOI18N
        modbusPanel.add(jLabel18);

        portField.setColumns(3);
        portField.setText(bundle.getString("PacketFrame.portField.text")); // NOI18N
        portField.setToolTipText(bundle.getString("PacketFrame.portField.toolTipText")); // NOI18N
        modbusPanel.add(portField);

        jLabel2.setText(bundle.getString("PacketFrame.jLabel2.text")); // NOI18N
        modbusPanel.add(jLabel2);

        slaveNodeField.setColumns(3);
        modbusPanel.add(slaveNodeField);

        jLabel5.setText(bundle.getString("PacketFrame.jLabel5.text")); // NOI18N
        modbusPanel.add(jLabel5);

        jLayeredPane1.setLayout(new java.awt.CardLayout());

        jPanel5.setPreferredSize(new java.awt.Dimension(250, 30));
        jPanel5.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT, 0, 0));

        functionCodeField.setColumns(3);
        functionCodeField.setPreferredSize(new java.awt.Dimension(6, 26));
        jPanel5.add(functionCodeField);

        jLayeredPane1.add(jPanel5, "card4");

        functionSelector.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Read Coils (0x01)", "Read Discrete Inputs (0x02)", "Read Holding Registers (0x03)", "Read Input Registers (0x04)", "Preset (Write) Single Register (0x06)", "Write Coils (0x15)", "Write Holding Registers (0x16)" }));
        functionSelector.setSelectedIndex(2);
        functionSelector.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                functionSelectorActionPerformed(evt);
            }
        });
        jLayeredPane1.add(functionSelector, "card2");

        modbusPanel.add(jLayeredPane1);

        getContentPane().add(modbusPanel);

        modbusPanel2.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        jLabel6.setText(bundle.getString("PacketFrame.jLabel6.text")); // NOI18N
        modbusPanel2.add(jLabel6);

        registerField.setColumns(4);
        modbusPanel2.add(registerField);

        jLabel3.setText(bundle.getString("PacketFrame.jLabel3.text")); // NOI18N
        modbusPanel2.add(jLabel3);

        transactionField.setColumns(5);
        transactionField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                transactionFieldActionPerformed(evt);
            }
        });
        modbusPanel2.add(transactionField);

        jLabel4.setText(bundle.getString("PacketFrame.jLabel4.text")); // NOI18N
        modbusPanel2.add(jLabel4);

        protoIdField.setColumns(5);
        modbusPanel2.add(protoIdField);

        jLabel7.setText(bundle.getString("PacketFrame.jLabel7.text")); // NOI18N
        modbusPanel2.add(jLabel7);

        quantityField.setColumns(2);
        modbusPanel2.add(quantityField);

        getContentPane().add(modbusPanel2);
        getContentPane().add(jSeparator3);

        jLabel8.setText(bundle.getString("PacketFrame.jLabel8.text")); // NOI18N
        messagePanel.add(jLabel8);

        buttonGroup1.add(customMessageButton);
        customMessageButton.setSelected(true);
        customMessageButton.setText(bundle.getString("PacketFrame.customMessageButton.text")); // NOI18N
        customMessageButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                customMessageButtonActionPerformed(evt);
            }
        });
        messagePanel.add(customMessageButton);

        buttonGroup1.add(readFloatButton);
        readFloatButton.setText(bundle.getString("PacketFrame.readFloatButton.text")); // NOI18N
        readFloatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                readFloatButtonActionPerformed(evt);
            }
        });
        messagePanel.add(readFloatButton);

        buttonGroup1.add(asciiReadButton);
        asciiReadButton.setText(bundle.getString("PacketFrame.asciiReadButton.text")); // NOI18N
        asciiReadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                asciiReadButtonActionPerformed(evt);
            }
        });
        messagePanel.add(asciiReadButton);

        buttonGroup1.add(u16ReadButton);
        u16ReadButton.setText(bundle.getString("PacketFrame.u16ReadButton.text")); // NOI18N
        u16ReadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                u16ReadButtonActionPerformed(evt);
            }
        });
        messagePanel.add(u16ReadButton);

        buttonGroup1.add(u32ReadButton);
        u32ReadButton.setText(bundle.getString("PacketFrame.u32ReadButton.text")); // NOI18N
        u32ReadButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                u32ReadButtonActionPerformed(evt);
            }
        });
        messagePanel.add(u32ReadButton);

        getContentPane().add(messagePanel);

        transmitPacketButton.setText(bundle.getString("PacketFrame.transmitPacketButton.text")); // NOI18N
        transmitPacketButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                transmitPacketButtonActionPerformed(evt);
            }
        });
        messagePanel2.add(transmitPacketButton);

        getContentPane().add(messagePanel2);
        getContentPane().add(jSeparator4);

        interpPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        jLabel9.setText(bundle.getString("PacketFrame.jLabel9.text")); // NOI18N
        interpPanel.add(jLabel9);

        byteSwapCheckbox.setText(bundle.getString("PacketFrame.byteSwapCheckbox.text")); // NOI18N
        byteSwapCheckbox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                byteSwapCheckboxActionPerformed(evt);
            }
        });
        interpPanel.add(byteSwapCheckbox);

        wordSwapCheckbox.setText(bundle.getString("PacketFrame.wordSwapCheckbox.text")); // NOI18N
        wordSwapCheckbox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                wordSwapCheckboxActionPerformed(evt);
            }
        });
        interpPanel.add(wordSwapCheckbox);

        getContentPane().add(interpPanel);
        getContentPane().add(jSeparator5);

        responsePanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        valueLabel.setText(bundle.getString("PacketFrame.valueLabel.text")); // NOI18N
        responsePanel.add(valueLabel);

        writeParentPane.setLayout(new java.awt.CardLayout());

        writeValuePane.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        valueField.setColumns(8);
        valueField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                valueFieldKeyReleased(evt);
            }
            public void keyTyped(java.awt.event.KeyEvent evt) {
                valueFieldKeyTyped(evt);
            }
        });
        writeValuePane.add(valueField);

        writeParentPane.add(writeValuePane, "writeValuePane");

        boolPane.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

        buttonGroup3.add(offButton);
        offButton.setText(bundle.getString("PacketFrame.offButton.text")); // NOI18N
        boolPane.add(offButton);

        buttonGroup3.add(onButton);
        onButton.setSelected(true);
        onButton.setText(bundle.getString("PacketFrame.onButton.text")); // NOI18N
        boolPane.add(onButton);

        writeParentPane.add(boolPane, "boolPane");
        writeParentPane.add(blankPane, "blankPane");

        responsePanel.add(writeParentPane);
        ((java.awt.CardLayout)(writeParentPane.getLayout())).show(writeParentPane, "blankPane");

        bitsButton.setText(bundle.getString("PacketFrame.bitsButton.text")); // NOI18N
        bitsButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                bitsButtonActionPerformed(evt);
            }
        });
        responsePanel.add(bitsButton);
        bitsButton.setVisible(false);

        getContentPane().add(responsePanel);
        getContentPane().add(jSeparator6);

        jLabel10.setText(bundle.getString("PacketFrame.jLabel10.text")); // NOI18N
        packetPanel.add(jLabel10);

        rawTextBox.setEditable(false);
        rawTextBox.setColumns(20);
        rawTextBox.setFont(new java.awt.Font("Monospaced", 0, 14)); // NOI18N
        rawTextBox.setRows(6);
        jScrollPane1.setViewportView(rawTextBox);

        packetPanel.add(jScrollPane1);

        getContentPane().add(packetPanel);

        jMenu2.setText(bundle.getString("PacketFrame.jMenu2.text")); // NOI18N

        jMenuItem1.setText(bundle.getString("PacketFrame.jMenuItem1.text")); // NOI18N
        jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem1ActionPerformed(evt);
            }
        });
        jMenu2.add(jMenuItem1);

        rtuSerialMonitorItem.setText(bundle.getString("PacketFrame.rtuSerialMonitorItem.text")); // NOI18N
        rtuSerialMonitorItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                rtuSerialMonitorItemActionPerformed(evt);
            }
        });
        jMenu2.add(rtuSerialMonitorItem);

        jMenuItem3.setText(bundle.getString("PacketFrame.jMenuItem3.text")); // NOI18N
        jMenuItem3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem3ActionPerformed(evt);
            }
        });
        jMenu2.add(jMenuItem3);

        jMenuItem2.setText(bundle.getString("PacketFrame.jMenuItem2.text")); // NOI18N
        jMenuItem2.setToolTipText(bundle.getString("PacketFrame.jMenuItem2.toolTipText")); // NOI18N
        jMenuItem2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem2ActionPerformed(evt);
            }
        });
        jMenu2.add(jMenuItem2);

        startModbusBridge.setText(bundle.getString("PacketFrame.startModbusBridge.text")); // NOI18N
        startModbusBridge.setToolTipText(bundle.getString("PacketFrame.startModbusBridge.toolTipText")); // NOI18N
        startModbusBridge.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startModbusBridgeActionPerformed(evt);
            }
        });
        jMenu2.add(startModbusBridge);

        jMenuBar1.add(jMenu2);

        jMenu1.setText(bundle.getString("PacketFrame.jMenu1.text")); // NOI18N

        addBookmarkItem.setText(bundle.getString("PacketFrame.addBookmarkItem.text")); // NOI18N
        addBookmarkItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addBookmarkItemActionPerformed(evt);
            }
        });
        jMenu1.add(addBookmarkItem);

        updateBookmarkItem.setText(bundle.getString("PacketFrame.updateBookmarkItem.text")); // NOI18N
        updateBookmarkItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                updateBookmarkItemActionPerformed(evt);
            }
        });
        jMenu1.add(updateBookmarkItem);

        deleteBookmarkItem.setText(bundle.getString("PacketFrame.deleteBookmarkItem.text")); // NOI18N
        deleteBookmarkItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                deleteBookmarkItemActionPerformed(evt);
            }
        });
        jMenu1.add(deleteBookmarkItem);

        bookmarksMenu.setText(bundle.getString("PacketFrame.bookmarksMenu.text")); // NOI18N
        bookmarksMenu.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                bookmarksMenuStateChanged(evt);
            }
        });
        bookmarksMenu.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                bookmarksMenuActionPerformed(evt);
            }
        });
        jMenu1.add(bookmarksMenu);
        addBookmarkItems(bookmarksMenu);

        jMenuBar1.add(jMenu1);

        jMenu3.setText(bundle.getString("PacketFrame.jMenu3.text")); // NOI18N

        jMenuItem4.setText(bundle.getString("PacketFrame.jMenuItem4.text")); // NOI18N
        jMenuItem4.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem4ActionPerformed(evt);
            }
        });
        jMenu3.add(jMenuItem4);

        jMenuItem5.setText(bundle.getString("PacketFrame.jMenuItem5.text")); // NOI18N
        jMenuItem5.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem5ActionPerformed(evt);
            }
        });
        jMenu3.add(jMenuItem5);

        jMenuBar1.add(jMenu3);

        setJMenuBar(jMenuBar1);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void destHostFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_destHostFieldActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_destHostFieldActionPerformed

    private void transactionFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_transactionFieldActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_transactionFieldActionPerformed

    private void transmitPacketButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_transmitPacketButtonActionPerformed
        //todo no thread dispatched when this action is performed which causes the UI to freeze for a few seconds
        lastResponse = null;
        int slaveNode = Integer.parseInt(slaveNodeField.getText());
        int register = Integer.parseInt(registerField.getText());
        int functionCode = 0;
        int quantity = 0;
        int transactionId = 1;
        int protocolId = 0;
        int tcpPort = 502;
        try
        {
            tcpPort = Integer.parseInt(portField.getText());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        boolean writingFlag = false;
        boolean coilsFlag = false;
        byte[] values = null;
        if (functionSelector.getSelectedItem().equals("Read Holding Registers (0x03)"))
        {
            functionCode = ModbusMechanic.READ_HOLDING_REGISTER_CODE;
        }
        if (functionSelector.getSelectedItem().equals("Read Input Registers (0x04)"))
        {
            functionCode = ModbusMechanic.READ_INPUT_REGISTER_CODE;
        }
        if (functionSelector.getSelectedItem().equals("Read Coils (0x01)"))
        {
            functionCode = ModbusMechanic.READ_COILS_CODE;
        }
        if (functionSelector.getSelectedItem().equals("Read Discrete Inputs (0x02)"))
        {
            functionCode = ModbusMechanic.READ_DI_CODE;
        }
        if (functionSelector.getSelectedItem().equals("Preset (Write) Single Register (0x06)"))
        {
            functionCode = ModbusMechanic.WRITE_SINGLE_REGISTER_CODE;
            writingFlag = true;
        }
        if (functionSelector.getSelectedItem().equals("Write Holding Registers (0x16)"))
        {
            functionCode = ModbusMechanic.WRITE_HOLDING_REGISTERS_CODE;
            writingFlag = true;
        }
        if (functionSelector.getSelectedItem().equals("Write Coils (0x15)"))
        {
            functionCode = ModbusMechanic.WRITE_COILS_CODE;
            writingFlag = true;
            coilsFlag = true;
        }
        if (customMessageButton.isSelected() && customMessageButton.isEnabled())
        {
            if (!rtuMsgButton.isSelected())
            {
                transactionId = Integer.parseInt(transactionField.getText());
                protocolId = Integer.parseInt(protoIdField.getText());
            }
            quantity = Integer.parseInt(quantityField.getText());
        }
        lastFunctionCode = functionCode;
        quantity = Integer.parseInt(quantityField.getText());
        if (writingFlag && !coilsFlag)
        {
            if (readFloatButton.isSelected())
            {
                float floatValue = Float.parseFloat(valueField.getText());
                values = java.nio.ByteBuffer.allocate(4).putFloat(floatValue).array();
                values = ModbusMechanic.wordSwap(values);
            }
            if (u16ReadButton.isSelected())
            {
                int intValue = Integer.parseInt(valueField.getText());
                values = java.nio.ByteBuffer.allocate(4).putInt(intValue).array();
                values = java.util.Arrays.copyOfRange(values, 2, 4);
            }
            if (u32ReadButton.isSelected())
            {
                long intValue = Long.parseLong(valueField.getText());
                values = java.nio.ByteBuffer.allocate(8).putLong(intValue).array();
                values = java.util.Arrays.copyOfRange(values, 4, 8);
            }
            if (asciiReadButton.isSelected())
            {
                values = valueField.getText().getBytes();
                if (values.length % 2 == 1)
                {
                    values = Arrays.copyOf(values, values.length+1);
                }
            }
            if (byteSwapCheckbox.isSelected())
            {
                values = ModbusMechanic.byteSwap(values);
            }
            if (wordSwapCheckbox.isSelected())
            {
                values = ModbusMechanic.wordSwap(values);
            }
        }
        if (coilsFlag)
        {
            values = new byte[] { 0x00 };
            if (onButton.isSelected())
            {
                values = new byte[] { 0x01 };
            }
        }
        if (rtuMsgButton.isSelected())
        {
            try
            {
                //todo error check user's input here
                lastResponse = ModbusMechanic.generateModbusRTURequest(comPortSelector.getItemAt(comPortSelector.getSelectedIndex()), Integer.parseInt(baudRateSelector.getItemAt(baudRateSelector.getSelectedIndex())), Integer.parseInt(dataBitsField.getText()), Integer.parseInt(stopBitsField.getText()), paritySelector.getSelectedIndex(), slaveNode, functionCode, register, quantity, values);
            }
            catch (Exception e)
            {
                handleModbusException(e);
            }
        }
        if (tcpMsgButton.isSelected())
        {
            try
            {
                lastResponse = ModbusMechanic.generateModbusTCPRequest(destHostField.getText(), tcpPort, protocolId, transactionId, slaveNode, functionCode, register, quantity, values);
            }
            catch (Exception e)
            {
                handleModbusException(e);
            }
        }
        //todo: lastResponseType is referenced from the current class, but the response definitions are referenced statically from MM class
        if (readFloatButton.isSelected())
        {
            lastResponseType = ModbusMechanic.RESPONSE_TYPE_FLOAT;
        }
        if (asciiReadButton.isSelected())
        {
            lastResponseType = ModbusMechanic.RESPONSE_TYPE_ASCII;
        }
        if (u16ReadButton.isSelected())
        {
            lastResponseType = ModbusMechanic.RESPONSE_TYPE_UINT16;
        }
        if (u32ReadButton.isSelected())
        {
            lastResponseType = ModbusMechanic.RESPONSE_TYPE_UINT32;
        }
        if (customMessageButton.isSelected())
        {
            lastResponseType = ModbusMechanic.RESPONSE_TYPE_RAW;
        }
        if (functionCode == 1 || functionCode == 2)
        {
            lastResponseType = ModbusMechanic.RESPONSE_TYPE_BOOLEAN;
        }
        if (lastResponse == null)
        {
            clearResponse();
        }
        else
        {
            displayResponse();
            displayRaw();
        }
    }//GEN-LAST:event_transmitPacketButtonActionPerformed
    public String[] getBookmarkNames()
    {
        bookmarkList.clear();
        String[] names = new String[] {""};
        try
        {
            BufferedReader br = new BufferedReader(new FileReader(new File("bookmarks.csv")));
            String line = null;
            //skip first header line
            br.readLine();
            line = br.readLine();
            while (line != null)
            {
                //storing by position for now friendlyname,tcp/rtu,port,baud,databits,stopbits,parity,ip,slavenode,function,register,transaction,protocolid,quantity,messagetype,byteswap,wordswap
                String[] bookmark = line.split(",");
                if (bookmark.length== 17)
                {
                    bookmarkList.add(bookmark);
                }
                line = br.readLine();
            }
            br.close();
            names = new String[bookmarkList.size()];
            for (int i = 0; i < names.length; i++)
            {
                names[i] = bookmarkList.get(i)[0];
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        return names;
    }
    public void addBookmarkItems(JMenu theMenu)
    {
        String[] names = getBookmarkNames();
        bookmarksMenu.removeAll();
        for (int i = 0; i < names.length; i++)
        {
            JCheckBoxMenuItem currentItem = new JCheckBoxMenuItem(names[i]);
            currentItem.addActionListener(new java.awt.event.ActionListener()
            {
                public void actionPerformed(java.awt.event.ActionEvent e)
                {
                    java.awt.Component[] menuItems = bookmarksMenu.getMenuComponents();
                    for (int i =0; i < menuItems.length; i++)
                    {
                        if (menuItems[i] != currentItem)
                        {
                            ((JCheckBoxMenuItem)(menuItems[i])).setSelected(false);
                        }
                        else
                        {
                            fireBookmarkEvent(getSelectedBookmarkIndex());
                        }
                    }
                } 
            });
            theMenu.add(currentItem);
        }
    }
    
    public byte[] getLastResponseBytes()
    {
        if (lastFunctionCode == ModbusMechanic.READ_HOLDING_REGISTER_CODE)
        {
            //todo all references to the underlying library should be done in the modbus mechanic class
            return ((ReadHoldingRegistersResponse)lastResponse).getBytes();
        }
        if (lastFunctionCode == ModbusMechanic.READ_INPUT_REGISTER_CODE)
        {
            return ((ReadInputRegistersResponse)lastResponse).getBytes();
        }
        if (lastFunctionCode == ModbusMechanic.READ_COILS_CODE)
        {
            return ((ReadCoilsResponse)lastResponse).getBytes();
        }
        if (lastFunctionCode == ModbusMechanic.READ_DI_CODE)
        {
            return ((ReadDiscreteInputsResponse)lastResponse).getBytes();
        }
        return null;
    }
    public void displayResponse()
    {
        byte[] result = getLastResponseBytes();
        if (result != null)
        {
            if (byteSwapCheckbox.isSelected())
            {
                result = ModbusMechanic.byteSwap(result); 
            }
            if (wordSwapCheckbox.isSelected())
            {
                result = ModbusMechanic.wordSwap(result); 
            }
            if (lastResponseType == ModbusMechanic.RESPONSE_TYPE_ASCII)
            {
                valueLabel.setText("Response value: " + new String(result));
            }
            if (lastResponseType == ModbusMechanic.RESPONSE_TYPE_FLOAT)
            {
                 valueLabel.setText("Response value: " + DataUtils.toFloat(result));
            }
            if (lastResponseType == ModbusMechanic.RESPONSE_TYPE_UINT16)
            {
                 valueLabel.setText("Response value: " + DataUtils.BeToIntArray(result)[0]);
            }
            if (lastResponseType == ModbusMechanic.RESPONSE_TYPE_UINT32)
            {
                 valueLabel.setText("Response value: " + ModbusMechanic.bytesToInt32(result));
            }
            if (lastResponseType == ModbusMechanic.RESPONSE_TYPE_BOOLEAN)
            {
                String boolValue = "false";
                if (getLastResponseBytes()[0] == 0x1)
                {
                    boolValue =  "true";
                }
                valueLabel.setText("Response value: " + boolValue);
            }
            bitsButton.setVisible(true);
        }
    }
    public void displayRaw()
    {
        StringBuffer sb = new StringBuffer();
        if (tcpMsgButton.isSelected())
        {
            sb.append("Transaction ID: " + lastResponse.getTransactionId() + "\n");
            sb.append("Protocol ID: " + lastResponse.getProtocolId() + "\n");
        }
        sb.append("Function code: " + lastFunctionCode + "\n");
        
        if (getLastResponseBytes() != null)
        {
            sb.append("Words:\n");
            byte[] responseBytes = getLastResponseBytes();
            if (responseBytes.length == 1)
            {
                sb.append(ModbusMechanic.byteToHex(new byte[] {responseBytes[0]}));
            }
            else
            {
                for (int i = 0; i < responseBytes.length; i = i + 2)
                {
                    sb.append(ModbusMechanic.byteToHex(new byte[] {responseBytes[i], responseBytes[i+1]}) + " ");
                    if ((i+2) % 4 == 0)
                    {
                        sb.append("\n");
                    }
                }
            }
        }
        rawTextBox.setText(sb.toString());
    }
    public void clearResponse()
    {
        valueLabel.setText("Response value: ");
        rawTextBox.setText("");
    }
    public void handleModbusException(Exception e)
    {
            if (e instanceof ModbusProtocolException)
            {
                JOptionPane.showMessageDialog(this, e.getMessage(), "Protocol Error", JOptionPane.ERROR_MESSAGE);
            }
            if (e instanceof ModbusNumberException)
            {
                e.printStackTrace();
            }
            if (e instanceof ModbusIOException)
            {
                if (e.getMessage().contains("purejavacomm.PortInUseException"))    
                {
                    JOptionPane.showMessageDialog(this, "Port in use or insufficient permissions. If the port is not in use running as administrator or root can elevate permissions.", "Serial port error", JOptionPane.ERROR_MESSAGE);
                }
                else
                {
                    JOptionPane.showMessageDialog(this, e.getMessage(), "IO Error", JOptionPane.ERROR_MESSAGE);
                }
            }
            if (ModbusMechanic.debug)
            {
                e.printStackTrace();
            }
    }
    public String parseFieldToText(String field)
    {
        try
        {
            Integer.parseInt(field);
            //as long as it is an int we get here
            return field;
        }
        catch (NumberFormatException e)
        {
            System.err.println("Unparsable bookmark field found");
        }
        return "";
    }
    private void customMessageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_customMessageButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_customMessageButtonActionPerformed

    private void readFloatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_readFloatButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_readFloatButtonActionPerformed

    private void functionSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_functionSelectorActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_functionSelectorActionPerformed

    private void asciiReadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_asciiReadButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_asciiReadButtonActionPerformed
    public int getSelectedBookmarkIndex()
    {
        java.awt.Component[] menuItems = bookmarksMenu.getMenuComponents();
        for (int i = 0; i < menuItems.length; i++)
        {
            if (((JCheckBoxMenuItem)(menuItems[i])).isSelected())
            {
                return i;
            }
        }
        return -1;
    }
    public void fireBookmarkEvent(int selectedBookmark)
    {
        String[] currentBookmark = bookmarkList.get(selectedBookmark);
        
        if (currentBookmark[1].equalsIgnoreCase("tcp"))
        {
            tcpMsgButton.doClick();
        }
        if (currentBookmark[1].equalsIgnoreCase("rtu"))
        {
            //this takes care of the corresponding "greying out"
            rtuMsgButton.doClick();
        }
        for (int i = 0; i < comPortSelector.getItemCount(); i++)
        {
            if (comPortSelector.getItemAt(i).equalsIgnoreCase(currentBookmark[2]))
            {
                comPortSelector.setSelectedIndex(i);
            }
        }
        if (currentBookmark[3].equals("4800"))
        {
            baudRateSelector.setSelectedIndex(0);
        }
        if (currentBookmark[3].equals("9600"))
        {
            baudRateSelector.setSelectedIndex(1);
        }
        if (currentBookmark[3].equals("14400"))
        {
            baudRateSelector.setSelectedIndex(2);
        }
        if (currentBookmark[3].equals("19200"))
        {
            baudRateSelector.setSelectedIndex(3);
        }
        if (currentBookmark[3].equals("38400"))
        {
            baudRateSelector.setSelectedIndex(4);
        }
        if (currentBookmark[3].equals("57600"))
        {
            baudRateSelector.setSelectedIndex(5);
        }
        dataBitsField.setText(parseFieldToText(currentBookmark[4]));
        stopBitsField.setText(parseFieldToText(currentBookmark[5]));
        if (currentBookmark[6].equalsIgnoreCase("none"))
        {
            paritySelector.setSelectedIndex(0);
        }
        if (currentBookmark[6].equalsIgnoreCase("odd"))
        {
            paritySelector.setSelectedIndex(1);
        }
        if (currentBookmark[6].equalsIgnoreCase("even"))
        {
            paritySelector.setSelectedIndex(2);
        }
        if (currentBookmark[6].equalsIgnoreCase("mark"))
        {
            paritySelector.setSelectedIndex(3);
        }
        if (currentBookmark[6].equalsIgnoreCase("space"))
        {
            paritySelector.setSelectedIndex(4);
        }
        if (destHostField.isEnabled())
        {
            destHostField.setText(currentBookmark[7]);
        }
        slaveNodeField.setText(parseFieldToText(currentBookmark[8]));
        functionCodeField.setText(parseFieldToText(currentBookmark[9]));
        if (currentBookmark[9].equals("3"))
        {
            functionSelector.setSelectedIndex(2);
            fireSelectionEvent();
        }
        if (currentBookmark[9].equals("4"))
        {
            functionSelector.setSelectedIndex(3);
            fireSelectionEvent();
        }
        registerField.setText(parseFieldToText(currentBookmark[10]));
        transactionField.setText(parseFieldToText(currentBookmark[11]));
        protoIdField.setText(parseFieldToText(currentBookmark[12]));
        quantityField.setText(parseFieldToText(currentBookmark[13]));
        if (currentBookmark[14].equalsIgnoreCase("float"))
        {
            readFloatButton.doClick();
        }
        if (currentBookmark[14].equalsIgnoreCase("u16"))
        {
            u16ReadButton.doClick();
        }
        if (currentBookmark[14].equalsIgnoreCase("u32"))
        {
            u32ReadButton.doClick();
        }
        if (currentBookmark[14].equalsIgnoreCase("ascii"))
        {
            asciiReadButton.doClick();
        }
        if (currentBookmark[14].equalsIgnoreCase("custom"))
        {
            customMessageButton.doClick();
        }
        if (currentBookmark[15].equals("1"))
        {
           byteSwapCheckbox.setSelected(true);
        }
        if (currentBookmark[15].equals("0"))
        {
           byteSwapCheckbox.setSelected(false);
        }
        if (currentBookmark[16].equals("1"))
        {
           wordSwapCheckbox.setSelected(true);
        }
        if (currentBookmark[16].equals("0"))
        {
           wordSwapCheckbox.setSelected(false);
        }
    }
    public void saveBookmark(int addIndex, String friendlyName)
    {
        
        String[] currentBookmark = new String[17];
        
        currentBookmark[0] = friendlyName;
        if (tcpMsgButton.isSelected())
        {
            currentBookmark[1] = "tcp";
        }
        if (rtuMsgButton.isSelected())
        {
            currentBookmark[1] = "rtu";
        }
        String comPort = comPortSelector.getItemAt(comPortSelector.getSelectedIndex());
        if (comPort == null)
        {
            comPort = "";
        }
        currentBookmark[2] = comPort;
        if (baudRateSelector.getSelectedIndex() == 0)
        {
            currentBookmark[3] = "4800";
        }
        if (baudRateSelector.getSelectedIndex() == 1)
        {
            currentBookmark[3] = "9600";
        }
        if (baudRateSelector.getSelectedIndex() == 2)
        {
            currentBookmark[3] = "14400";
        }
        if (baudRateSelector.getSelectedIndex() == 3)
        {
            currentBookmark[3] = "19200";
        }
        if (baudRateSelector.getSelectedIndex() == 4)
        {
            currentBookmark[3] = "38400";
        }
        if (baudRateSelector.getSelectedIndex() == 5)
        {
            currentBookmark[3] = "57600";
        }
        currentBookmark[4] = dataBitsField.getText();
        currentBookmark[5] = stopBitsField.getText();
        if (paritySelector.getSelectedIndex() == 0)
        {
            currentBookmark[6] = "none";
        }
        if (paritySelector.getSelectedIndex() == 1)
        {
            currentBookmark[6] = "odd";
        }
        if (paritySelector.getSelectedIndex() == 2)
        {
            currentBookmark[6] = "even";
        }
        if (paritySelector.getSelectedIndex() == 3)
        {
            currentBookmark[6] = "mark";
        }
        if (paritySelector.getSelectedIndex() == 4)
        {
            currentBookmark[6] = "space";
        }
        String destHost = "";
        if (destHostField.isEnabled())
        {
            destHost = destHostField.getText();
        }
        currentBookmark[7] = destHost;
        currentBookmark[8] = slaveNodeField.getText();
        if (functionCodeField.isVisible())
        {
            currentBookmark[9] = functionCodeField.getText();
        }
        else
        {
            if (functionSelector.getSelectedIndex() == 2)
            {
                currentBookmark[9] = "3";
            }
            if (functionSelector.getSelectedIndex() == 3)
            {
                currentBookmark[9] = "4";
            }
        }
        currentBookmark[10] = registerField.getText();
        currentBookmark[11] = transactionField.getText();
        currentBookmark[12] = protoIdField.getText();
        currentBookmark[13] = quantityField.getText();
        if(readFloatButton.isSelected())
        {
            currentBookmark[14] = "float";
        }
        if(u16ReadButton.isSelected())
        {
            currentBookmark[14] = "u16";
        }
        if(u32ReadButton.isSelected())
        {
            currentBookmark[14] = "u32";
        }
        if(asciiReadButton.isSelected())
        {
            currentBookmark[14] = "ascii";
        }
        if(customMessageButton.isSelected())
        {
            currentBookmark[14] = "custom";
        }
        String byteSwap = "0";
        String wordSwap = "0";
        if (byteSwapCheckbox.isSelected())
        {
            byteSwap = "1";
        }
        if (wordSwapCheckbox.isSelected())
        {
            wordSwap = "1";
        }
        currentBookmark[15] = byteSwap;
        currentBookmark[16] = wordSwap;
        bookmarkList.add(addIndex, currentBookmark);
        
        writeBookmarks();
    }
    public void saveBookmark(String friendlyName)
    {
        saveBookmark(bookmarkList.size(), friendlyName);
    }
    public boolean writeBookmarks()
    {
        try
        {
            PrintWriter pw = new PrintWriter(new FileWriter("bookmarks.csv"));
            pw.println("friendlyname,tcp/rtu,port,baud,databits,stopbits,parity,ip,slavenode,function,register,transaction,protocolid,quantity,messagetype,byteswap,wordswap");
            for (int i = 0; i < bookmarkList.size(); i++)
            {
                String theLine = bookmarkList.get(i)[0];
                for (int j = 1; j < bookmarkList.get(i).length; j++)
                {
                    theLine = theLine + "," + bookmarkList.get(i)[j];
                }
                pw.println(theLine);
            }
            pw.flush();
            pw.close();
        }
        catch(IOException e)
        {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    public void deleteBookmark(int bookmarkIndex)
    {
            bookmarkList.remove(bookmarkIndex);
            writeBookmarks();
            addBookmarkItems(bookmarksMenu);
    }
    public void fireSelectionEvent()
    {
        if(tcpMsgButton.isSelected())
        {
            protoIdField.setEnabled(true);
            transactionField.setEnabled(true);
            destHostField.setEnabled(true);
            portField.setEnabled(true);
            displayTtySerial.setEnabled(false);
        }
        if(rtuMsgButton.isSelected())
        {
            destHostField.setEnabled(false);
            protoIdField.setEnabled(false);
            transactionField.setEnabled(false);
            portField.setEnabled(false);
            displayTtySerial.setEnabled(true);
        }
        valueLabel.setText("Response value:");
        ((java.awt.CardLayout)(writeParentPane.getLayout())).show(writeParentPane, "blankPane");
        boolean readFlag = false;
        boolean writeFlag = false;
        boolean coilsFlag = false;
        boolean wordsFlag = false;
        boolean singleLength = false;
        bitsButton.setVisible(false);
        if (functionSelector.getSelectedItem().equals("Read Coils (0x01)") || functionSelector.getSelectedItem().equals("Read Discrete Inputs (0x02)") || functionSelector.getSelectedItem().equals("Write Coils (0x15)"))
        {
            coilsFlag = true;
        }
        if (functionSelector.getSelectedItem().toString().contains("Write"))
        {
            writeFlag = true;
        }
        if (functionSelector.getSelectedItem().equals("Read Holding Registers (0x03)") || functionSelector.getSelectedItem().equals("Read Input Registers (0x04)") || functionSelector.getSelectedItem().equals("Write Holding Registers (0x16)"))
        {
            wordsFlag = true;
        }
        if (functionSelector.getSelectedItem().equals("Preset (Write) Single Register (0x06)"))
        {
            singleLength = true;
        }
        if (coilsFlag)
        {
            customMessageButton.setEnabled(false);
            readFloatButton.setEnabled(false);
            u16ReadButton.setEnabled(false);
            u32ReadButton.setEnabled(false);
            asciiReadButton.setEnabled(false);
            transactionField.setEnabled(false);
            protoIdField.setEnabled(false);
            quantityField.setEnabled(false);
            wordSwapCheckbox.setEnabled(false);
            byteSwapCheckbox.setEnabled(false);
            quantityField.setText("1");
        }
        if (writeFlag && !coilsFlag)
        {
            if (customMessageButton.isSelected())
            {
                u16ReadButton.setSelected(true);
            }
            valueLabel.setText("Write value:");
            ((java.awt.CardLayout)(writeParentPane.getLayout())).show(writeParentPane, "writeValuePane");
            if (u16ReadButton.isSelected() || u32ReadButton.isSelected())
            {
                bitsButton.setVisible(true);
            }
        }
        if (coilsFlag && writeFlag)
        {
            ((java.awt.CardLayout)(writeParentPane.getLayout())).show(writeParentPane, "boolPane");
        }
        if (singleLength)
        {
            if (!customMessageButton.isSelected() && !u16ReadButton.isSelected())
            {
                customMessageButton.setSelected(true);
            }
            readFloatButton.setEnabled(false);
            asciiReadButton.setEnabled(false);
            u32ReadButton.setEnabled(false);
            quantityField.setText("1");
            quantityField.setEnabled(false);
            if (u16ReadButton.isSelected())
            {
                transactionField.setEnabled(false);
                protoIdField.setEnabled(false);
            }
        }
        if (wordsFlag)
        {
            customMessageButton.setEnabled(true);
            readFloatButton.setEnabled(true);
            u16ReadButton.setEnabled(true);
            u32ReadButton.setEnabled(true);
            asciiReadButton.setEnabled(true);
            if (customMessageButton.isSelected())
            {
                if (!rtuMsgButton.isSelected())
                {
                    transactionField.setEnabled(true);
                    protoIdField.setEnabled(true);
                }
                quantityField.setEnabled(true);
            }
            if (readFloatButton.isSelected())
            {
                protoIdField.setEnabled(false);
                transactionField.setEnabled(false);
                quantityField.setEnabled(false);
                quantityField.setText("2");

            }
            if (asciiReadButton.isSelected())
            {
                protoIdField.setEnabled(false);
                transactionField.setEnabled(false);
                quantityField.setEnabled(true);
                jPanel5.setVisible(false);
                functionSelector.setVisible(true);
            }
            if (asciiReadButton.isSelected() && functionSelector.getSelectedItem().equals("Write Holding Registers (0x16)"))
            {
                quantityField.setEnabled(false);
            }
            if (u16ReadButton.isSelected())
            {
                protoIdField.setEnabled(false);
                transactionField.setEnabled(false);
                quantityField.setEnabled(false);
                quantityField.setText("1");
            }
            if (u32ReadButton.isSelected())
            {
                protoIdField.setEnabled(false);
                transactionField.setEnabled(false);
                quantityField.setEnabled(false);
                quantityField.setText("2");
            }
            wordSwapCheckbox.setEnabled(true);
            byteSwapCheckbox.setEnabled(true);
        }
        
    }
    
    private void byteSwapCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_byteSwapCheckboxActionPerformed
        displayResponse();
    }//GEN-LAST:event_byteSwapCheckboxActionPerformed

    private void wordSwapCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_wordSwapCheckboxActionPerformed
        displayResponse();
    }//GEN-LAST:event_wordSwapCheckboxActionPerformed

    private void u16ReadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_u16ReadButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_u16ReadButtonActionPerformed

    private void u32ReadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_u32ReadButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_u32ReadButtonActionPerformed

    private void rtuMsgButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rtuMsgButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_rtuMsgButtonActionPerformed

    private void tcpMsgButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_tcpMsgButtonActionPerformed
        fireSelectionEvent();
    }//GEN-LAST:event_tcpMsgButtonActionPerformed

    private void paritySelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_paritySelectorActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_paritySelectorActionPerformed

    private void comPortSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comPortSelectorActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_comPortSelectorActionPerformed

    private void deleteBookmarkItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteBookmarkItemActionPerformed
        if (JOptionPane.showConfirmDialog(this, "Are you sure you want to delete this bookmark?", "Confirm Delete", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.YES_OPTION)
        {
            deleteBookmark(getSelectedBookmarkIndex());
        }
    }//GEN-LAST:event_deleteBookmarkItemActionPerformed

    private void updateBookmarkItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_updateBookmarkItemActionPerformed
        String friendlyName = JOptionPane.showInputDialog(this, "Enter a name for the bookmark:");
        if (friendlyName != null)
        {
            if (!friendlyName.contains(","))
            {
                //delete and re add
                int selectedIndex = getSelectedBookmarkIndex();
                bookmarkList.remove(selectedIndex);
                saveBookmark(selectedIndex, friendlyName);
                addBookmarkItems(bookmarksMenu);
                ((JCheckBoxMenuItem)(bookmarksMenu.getMenuComponent(selectedIndex))).setSelected(true);
            }
            else
            {
                JOptionPane.showMessageDialog(this, "Commas in name not supported", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }//GEN-LAST:event_updateBookmarkItemActionPerformed

    private void addBookmarkItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addBookmarkItemActionPerformed
        String friendlyName = JOptionPane.showInputDialog(this, "Enter a name for the bookmark:");
        if (friendlyName != null)
        {
            if (!friendlyName.contains(","))
            {
                saveBookmark(friendlyName);
                addBookmarkItems(bookmarksMenu);
                ((JCheckBoxMenuItem)(bookmarksMenu.getMenuComponent(bookmarksMenu.getMenuComponents().length-1))).setSelected(true);
            }
            else
            {
                JOptionPane.showMessageDialog(this, "Commas in name not supported", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }//GEN-LAST:event_addBookmarkItemActionPerformed

    private void bookmarksMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bookmarksMenuActionPerformed
        
    }//GEN-LAST:event_bookmarksMenuActionPerformed

    private void bookmarksMenuStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_bookmarksMenuStateChanged
        
    }//GEN-LAST:event_bookmarksMenuStateChanged

    private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem1ActionPerformed
        int simulatorType = JOptionPane.showOptionDialog(this, "What type of simulator?", "Simulator type", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, new String[] {"TCP", "RTU" }, null);
        int slaveId = Integer.parseInt(JOptionPane.showInputDialog(this, "Enter the Slave ID", "1"));
        SlaveSimulatorFrame theFrame = new SlaveSimulatorFrame(slaveId);
        if (simulatorType == 0)
        {
            ModbusMechanic.startSlaveSimulatorTCP(502, theFrame.getRegisterList());
        }
        else
        {
            ModbusMechanic.startSlaveSimulatorRTU(slaveId, comPortSelector.getItemAt(comPortSelector.getSelectedIndex()), Integer.parseInt(baudRateSelector.getItemAt(baudRateSelector.getSelectedIndex())), Integer.parseInt(dataBitsField.getText()), Integer.parseInt(stopBitsField.getText()), paritySelector.getSelectedIndex());
        }
        theFrame.setVisible(true);
        
    }//GEN-LAST:event_jMenuItem1ActionPerformed

    private void rtuSerialMonitorItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rtuSerialMonitorItemActionPerformed
        ModbusMechanic.startSerialMonitorFrame(comPortSelector.getItemAt(comPortSelector.getSelectedIndex()), Integer.parseInt(baudRateSelector.getItemAt(baudRateSelector.getSelectedIndex())), Integer.parseInt(dataBitsField.getText()), Integer.parseInt(stopBitsField.getText()), paritySelector.getSelectedIndex());
    }//GEN-LAST:event_rtuSerialMonitorItemActionPerformed

    private void valueFieldKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_valueFieldKeyTyped
        
    }//GEN-LAST:event_valueFieldKeyTyped

    private void valueFieldKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_valueFieldKeyReleased
        if (asciiReadButton.isSelected())
        {
            quantityField.setText((int)(Math.ceil((double)(valueField.getText().length())/2)) + "");
        }
    }//GEN-LAST:event_valueFieldKeyReleased

    private void bitsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bitsButtonActionPerformed
        if (functionSelector.getSelectedItem().equals("Write Holding Registers (0x16)"))
        {
            if (u16ReadButton.isSelected())
            {
                bitsFrame = new BitsFrame(valueField, 16);
                int int16Value = 0;
                try
                {
                    int16Value = Integer.parseInt(valueField.getText());
                }
                catch(NumberFormatException e)
                {
                    e.printStackTrace();
                }
                bitsFrame.displayInt16(int16Value);
            }
            if (u32ReadButton.isSelected())
            {
                bitsFrame = new BitsFrame(valueField, 32);
                long int32Value = 0;
                try
                {
                    int32Value = Long.parseLong(valueField.getText());
                }
                catch(NumberFormatException e)
                {
                    e.printStackTrace();
                }
                bitsFrame.displayInt32(int32Value);
            }
        }
        else
        {
            bitsFrame = new BitsFrame(getLastResponseBytes());
            bitsFrame.calculateBytes();
        }
        bitsFrame.setVisible(true);
    }//GEN-LAST:event_bitsButtonActionPerformed

    private void jMenuItem2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem2ActionPerformed
        (new GatewayFrame()).setVisible(true);
    }//GEN-LAST:event_jMenuItem2ActionPerformed

    private void displayTtySerialActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_displayTtySerialActionPerformed
        fireSelectionEvent();
        comPortSelector.setModel(getPortNames());
    }//GEN-LAST:event_displayTtySerialActionPerformed

    private void startModbusBridgeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startModbusBridgeActionPerformed
        JOptionPane.showMessageDialog(this, "Warning: this functionality has been moved to ProtocolWhisperer and will be removed from ModbusMechanic in future versions.", "Warning", JOptionPane.WARNING_MESSAGE);
        new modbusmechanic.bridge.BridgeManager(false, "");
    }//GEN-LAST:event_startModbusBridgeActionPerformed

    private void jMenuItem3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem3ActionPerformed
        (new RTUScannerFrame()).setVisible(true);
    }//GEN-LAST:event_jMenuItem3ActionPerformed

    private void jMenuItem4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem4ActionPerformed
        UpdateFrame.updateCheck(false);
    }//GEN-LAST:event_jMenuItem4ActionPerformed

    private void jMenuItem5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem5ActionPerformed
        try
        {
            File f = new File(".updatecheckdisabled");
            f.delete();
        }
        catch (Exception e)
        {
            if (ModbusMechanic.debug)
            {
                e.printStackTrace();
            }
        }
    }//GEN-LAST:event_jMenuItem5ActionPerformed

    private DefaultComboBoxModel getPortNames()
    {
        String[] portNames =  ModbusMechanic.getPortNames();
        for (int i = 0; !hasTtyPorts && i < portNames.length; i++)
        {
            if (portNames[i].toLowerCase().startsWith("ttys"))
            {
                hasTtyPorts = true;
            }
        }
        displayTtySerial.setVisible(hasTtyPorts);
        if (hasTtyPorts && !displayTtySerial.isSelected())
        {
            ArrayList<String> filteredPorts = new ArrayList();
            for (int i = 0; i < portNames.length; i++)
            {
                if (!portNames[i].toLowerCase().startsWith("ttys"))
                {
                    filteredPorts.add(portNames[i]);
                }
            }
            return new DefaultComboBoxModel(filteredPorts.toArray());
        }
        return new DefaultComboBoxModel(portNames);
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        
        //</editor-fold>

        /* Create and display the form */
        
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem addBookmarkItem;
    private javax.swing.JRadioButton asciiReadButton;
    private javax.swing.JComboBox<String> baudRateSelector;
    private javax.swing.JButton bitsButton;
    private javax.swing.JPanel blankPane;
    private javax.swing.JMenu bookmarksMenu;
    private javax.swing.JPanel boolPane;
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.ButtonGroup buttonGroup2;
    private javax.swing.ButtonGroup buttonGroup3;
    private javax.swing.JCheckBox byteSwapCheckbox;
    private javax.swing.JComboBox<String> comPortSelector;
    private javax.swing.JRadioButton customMessageButton;
    private javax.swing.JTextField dataBitsField;
    private javax.swing.JMenuItem deleteBookmarkItem;
    private javax.swing.JTextField destHostField;
    private javax.swing.JCheckBox displayTtySerial;
    private javax.swing.JTextField functionCodeField;
    private javax.swing.JComboBox<String> functionSelector;
    private javax.swing.JPanel interpPanel;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel10;
    private javax.swing.JLabel jLabel11;
    private javax.swing.JLabel jLabel12;
    private javax.swing.JLabel jLabel13;
    private javax.swing.JLabel jLabel14;
    private javax.swing.JLabel jLabel15;
    private javax.swing.JLabel jLabel16;
    private javax.swing.JLabel jLabel17;
    private javax.swing.JLabel jLabel18;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JLabel jLabel9;
    private javax.swing.JLayeredPane jLayeredPane1;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenu jMenu2;
    private javax.swing.JMenu jMenu3;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JMenuItem jMenuItem2;
    private javax.swing.JMenuItem jMenuItem3;
    private javax.swing.JMenuItem jMenuItem4;
    private javax.swing.JMenuItem jMenuItem5;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JSeparator jSeparator3;
    private javax.swing.JSeparator jSeparator4;
    private javax.swing.JSeparator jSeparator5;
    private javax.swing.JSeparator jSeparator6;
    private javax.swing.JPanel messagePanel;
    private javax.swing.JPanel messagePanel2;
    private javax.swing.JPanel modbusPanel;
    private javax.swing.JPanel modbusPanel2;
    private javax.swing.JRadioButton offButton;
    private javax.swing.JRadioButton onButton;
    private javax.swing.JPanel packetPanel;
    private javax.swing.JComboBox<String> paritySelector;
    private javax.swing.JTextField portField;
    private javax.swing.JTextField protoIdField;
    private javax.swing.JTextField quantityField;
    private javax.swing.JTextArea rawTextBox;
    private javax.swing.JRadioButton readFloatButton;
    private javax.swing.JTextField registerField;
    private javax.swing.JPanel responsePanel;
    private javax.swing.JRadioButton rtuMsgButton;
    private javax.swing.JMenuItem rtuSerialMonitorItem;
    private javax.swing.JPanel serialPanel;
    private javax.swing.JTextField slaveNodeField;
    private javax.swing.JMenuItem startModbusBridge;
    private javax.swing.JTextField stopBitsField;
    private javax.swing.JRadioButton tcpMsgButton;
    private javax.swing.JTextField transactionField;
    private javax.swing.JButton transmitPacketButton;
    private javax.swing.JPanel typePanel;
    private javax.swing.JRadioButton u16ReadButton;
    private javax.swing.JRadioButton u32ReadButton;
    private javax.swing.JMenuItem updateBookmarkItem;
    private javax.swing.JTextField valueField;
    private javax.swing.JLabel valueLabel;
    private javax.swing.JCheckBox wordSwapCheckbox;
    private javax.swing.JPanel writeParentPane;
    private javax.swing.JPanel writeValuePane;
    // End of variables declaration//GEN-END:variables
}
